/* コンソール関係 */

#include "bootpack.h"
#include "elf.h"
#include <stdio.h>
#include <string.h>
#include "module.h"

struct symbol_table_item {
    const char *name;
    unsigned int address;
};

#define SYM(name) { "_" ## #name, (unsigned int)&name + 0x280000 }
static struct symbol_table_item symbol_table[] = {
    SYM(hrb_print),
    SYM(io_in8),
    SYM(io_out8),
    SYM(sprintf),
    { NULL, 0 }
};

void console_task(struct SHEET *sheet, int memtotal, int width, int height)
{
	struct TASK *task = task_now();
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	int i, *fat = (int *) memman_alloc_4k(memman, 4 * 2880);
	struct CONSOLE cons;
	struct FILEHANDLE fhandle[8];
	char cmdline[30];
	unsigned char *nihongo = (char *) *((int *) 0x0fe8);

	cons.sht = sheet;
	cons.width = width;
	cons.height = height;
	cons.cur_x =  8;
	cons.cur_y = 28;
	cons.cur_c = -1;
	task->cons = &cons;
	task->cmdline = cmdline;

	if (cons.sht != 0) {
		cons.timer = timer_alloc();
		timer_init(cons.timer, &task->fifo, 1);
		timer_settime(cons.timer, 50);
	}
	file_readfat(fat, (unsigned char *) (ADR_DISKIMG + 0x000200));
	for (i = 0; i < 8; i++) {
		fhandle[i].buf = 0;	/* 未使用マーク */
	}
	task->fhandle = fhandle;
	task->fat = fat;
	if (nihongo[4096] != 0xff) {	/* 日本語フォントファイルを読み込めたか？ */
		task->langmode = 1;
	} else {
		task->langmode = 0;
	}
	task->langbyte1 = 0;

	/* プロンプト表示 */
	cons_putchar(&cons, '>', 1);

	for (;;) {
		io_cli();
		if (fifo32_status(&task->fifo) == 0) {
			task_sleep(task);
			io_sti();
		} else {
			i = fifo32_get(&task->fifo);
			io_sti();
			if (i <= 1 && cons.sht != 0) { /* カーソル用タイマ */
				if (i != 0) {
					timer_init(cons.timer, &task->fifo, 0); /* 次は0を */
					if (cons.cur_c >= 0) {
						cons.cur_c = COL8_FFFFFF;
					}
				} else {
					timer_init(cons.timer, &task->fifo, 1); /* 次は1を */
					if (cons.cur_c >= 0) {
						cons.cur_c = COL8_000000;
					}
				}
				timer_settime(cons.timer, 50);
			}
			if (i == 2) {	/* カーソルON */
				cons.cur_c = COL8_FFFFFF;
			}
			if (i == 3) {	/* カーソルOFF */
				if (cons.sht != 0) {
					boxfill8(cons.sht->buf, cons.sht->bxsize, COL8_000000,
						cons.cur_x, cons.cur_y, cons.cur_x + 7, cons.cur_y + 15);
				}
				cons.cur_c = -1;
			}
			if (i == 4) {	/* コンソールの「×」ボタンクリック */
				cmd_exit(&cons, fat);
			}
			if (256 <= i && i <= 511) { /* キーボードデータ（タスクA経由） */
				if (i == 8 + 256) {
					/* バックスペース */
					if (cons.cur_x > 16) {
						/* カーソルをスペースで消してから、カーソルを1つ戻す */
						cons_putchar(&cons, ' ', 0);
						cons.cur_x -= 8;
					}
				} else if (i == 10 + 256) {
					/* Enter */
					/* カーソルをスペースで消してから改行する */
					cons_putchar(&cons, ' ', 0);
					cmdline[cons.cur_x / 8 - 2] = 0;
					cons_newline(&cons);
					cons_runcmd(cmdline, &cons, fat, memtotal);	/* コマンド実行 */
					if (cons.sht == 0) {
						cmd_exit(&cons, fat);
					}
					/* プロンプト表示 */
					cons_putchar(&cons, '>', 1);
				} else {
					/* 一般文字 */
					if (cons.cur_x < cons.width * 30) {
						/* 一文字表示してから、カーソルを1つ進める */
						cmdline[cons.cur_x / 8 - 2] = i - 256;
						cons_putchar(&cons, i - 256, 1);
					}
				}
			}
			/* カーソル再表示 */
			if (cons.sht != 0) {
				if (cons.cur_c >= 0) {
					boxfill8(cons.sht->buf, cons.sht->bxsize, cons.cur_c, 
						cons.cur_x, cons.cur_y, cons.cur_x + 7, cons.cur_y + 15);
				}
				sheet_refresh(cons.sht, cons.cur_x, cons.cur_y, cons.cur_x + 8, cons.cur_y + 16);
			}
		}
	}
}

void cons_putchar(struct CONSOLE *cons, int chr, char move)
{
	char s[2];
	s[0] = chr;
	s[1] = 0;
	if (s[0] == 0x09) {	/* タブ */
		for (;;) {
			if (cons->sht != 0) {
				putfouts8_asc_sht(cons->sht, cons->cur_x, cons->cur_y, COL8_FFFFFF, COL8_000000, " ", 1);
			}
			cons->cur_x += 8;
			if (cons->cur_x == 8 + 8 * cons->width) {
				cons_newline(cons);
			}
			if (((cons->cur_x - 8) & 0x1f) == 0) {
				break;	/* 32で割り切れたらbreak */
			}
		}
	} else if (s[0] == 0x0a) {	/* 改行 */
		cons_newline(cons);
	} else if (s[0] == 0x0d) {	/* 復帰 */
		/* とりあえずなにもしない */
	} else {	/* 普通の文字 */
		if (cons->sht != 0) {
			putfouts8_asc_sht(cons->sht, cons->cur_x, cons->cur_y, COL8_FFFFFF, COL8_000000, s, 1);
		}
		if (move != 0) {
			/* moveが0のときはカーソルを進めない */
			cons->cur_x += 8;
			if (cons->cur_x == 8 + 8 * cons->width) {
				cons_newline(cons);
			}
		}
	}
	return;
}

void cons_newline(struct CONSOLE *cons)
{
	int x, y;
	struct SHEET *sheet = cons->sht;
	struct TASK *task = task_now();
	if (cons->cur_y < 28 + 16 * (cons->height - 1)) {
		cons->cur_y += 16; /* 次の行へ */
	} else {
		/* スクロール */
		if (sheet != 0) {
			for (y = 28; y < 28 + 16 * (cons->height - 1); y++) {
				for (x = 8; x < 8 + 8 * cons->width; x++) {
					sheet->buf[x + y * sheet->bxsize] = sheet->buf[x + (y + 16) * sheet->bxsize];
				}
			}
			for (y = 28 + 16 * (cons->height - 1); y < 28 + 16 * cons->height; y++) {
				for (x = 8; x < 8 + 8 * cons->width; x++) {
					sheet->buf[x + y * sheet->bxsize] = COL8_000000;
				}
			}
			sheet_refresh(sheet, 8, 28, 8 + 8 * cons->width, 28 + 16 * cons->height);
		}
	}
	cons->cur_x = 8;
	if (task->langmode == 1 && task->langbyte1 != 0) {
		cons->cur_x = 16;
	}
	return;
}

void cons_putstr0(struct CONSOLE *cons, char *s)
{
	for (; *s != 0; s++) {
		cons_putchar(cons, *s, 1);
	}
	return;
}

void cons_putstr1(struct CONSOLE *cons, char *s, int l)
{
	int i;
	for (i = 0; i < l; i++) {
		cons_putchar(cons, s[i], 1);
	}
	return;
}

void cons_runcmd(char *cmdline, struct CONSOLE *cons, int *fat, int memtotal)
{
	if (strcmp(cmdline, "mem") == 0 && cons->sht != 0) {
		cmd_mem(cons, memtotal);
	} else if (strcmp(cmdline, "cls") == 0 && cons->sht != 0) {
		cmd_cls(cons);
	} else if (strcmp(cmdline, "dir") == 0 && cons->sht != 0) {
		cmd_dir(cons);
	} else if (strcmp(cmdline, "exit") == 0) {
		cmd_exit(cons, fat);
	} else if (strncmp(cmdline, "start ", 6) == 0) {
		cmd_start(cons, cmdline, memtotal);
	} else if (strncmp(cmdline, "ncst ", 5) == 0) {
		cmd_ncst(cons, cmdline, memtotal);
	} else if (strncmp(cmdline, "langmode ", 9) == 0) {
		cmd_langmode(cons, cmdline);
	} else if (strncmp(cmdline, "insmod ", 7) == 0) {
		cmd_insmod(cons, fat, cmdline);
	} else if (cmdline[0] != 0) {
		if (cmd_app(cons, fat, cmdline) == 0) {
			/* コマンドではなく、アプリでもなく、さらに空行でもない */
			cons_putstr0(cons, "Bad command.\n\n");
		}
	}
	return;
}

void cmd_mem(struct CONSOLE *cons, int memtotal)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	char s[60];
	sprintf(s, "total   %dMB\nfree %dKB\n\n", memtotal / (1024 * 1024), memman_total(memman) / 1024);
	cons_putstr0(cons, s);
	return;
}

void cmd_cls(struct CONSOLE *cons)
{
	int x, y;
	struct SHEET *sheet = cons->sht;
	for (y = 28; y < 28 + 128; y++) {
		for (x = 8; x < 8 + 240; x++) {
			sheet->buf[x + y * sheet->bxsize] = COL8_000000;
		}
	}
	sheet_refresh(sheet, 8, 28, 8 + 8 * cons->width, 28 + 16 * cons->height);
	cons->cur_y = 28;
	return;
}

void cmd_dir(struct CONSOLE *cons)
{
	struct FILEINFO *finfo = (struct FILEINFO *) (ADR_DISKIMG + 0x002600);
	int i, j;
	char s[30];
	for (i = 0; i < 224; i++) {
		if (finfo[i].name[0] == 0x00) {
			break;
		}
		if (finfo[i].name[0] != 0xe5) {
			if ((finfo[i].type & 0x18) == 0) {
				sprintf(s, "filename.ext   %7d\n", finfo[i].size);
				for (j = 0; j < 8; j++) {
					s[j] = finfo[i].name[j];
				}
				s[ 9] = finfo[i].ext[0];
				s[10] = finfo[i].ext[1];
				s[11] = finfo[i].ext[2];
				cons_putstr0(cons, s);
			}
		}
	}
	cons_newline(cons);
	return;
}

void cmd_exit(struct CONSOLE *cons, int *fat)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct TASK *task = task_now();
	struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
	struct FIFO32 *fifo = (struct FIFO32 *) *((int *) 0x0fec);
	if (cons->sht != 0) {
		timer_cancel(cons->timer);
	}
	memman_free_4k(memman, (int) fat, 4 * 2880);
	io_cli();
	if (cons->sht != 0) {
		fifo32_put(fifo, cons->sht - shtctl->sheets0 + 768);	/* 768〜1023 */
	} else {
		fifo32_put(fifo, task - taskctl->tasks0 + 1024);	/* 1024〜2023 */
	}
	io_sti();
	for (;;) {
		task_sleep(task);
	}
}

void cmd_start(struct CONSOLE *cons, char *cmdline, int memtotal)
{
	struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
	struct SHEET *sht = open_console(shtctl, memtotal, 80, 24);
	struct FIFO32 *fifo = &sht->task->fifo;
	int i;
	sheet_slide(sht, 32, 4);
	sheet_updown(sht, shtctl->top);
	/* コマンドラインに入力された文字列を、一文字ずつ新しいコンソールに入力 */
	for (i = 6; cmdline[i] != 0; i++) {
		fifo32_put(fifo, cmdline[i] + 256);
	}
	fifo32_put(fifo, 10 + 256);	/* Enter */
	cons_newline(cons);
	return;
}

void cmd_ncst(struct CONSOLE *cons, char *cmdline, int memtotal)
{
	struct TASK *task = open_constask(0, memtotal, 80, 24);
	struct FIFO32 *fifo = &task->fifo;
	int i;
	/* コマンドラインに入力された文字列を、一文字ずつ新しいコンソールに入力 */
	for (i = 5; cmdline[i] != 0; i++) {
		fifo32_put(fifo, cmdline[i] + 256);
	}
	fifo32_put(fifo, 10 + 256);	/* Enter */
	cons_newline(cons);
	return;
}

void cmd_langmode(struct CONSOLE *cons, char *cmdline)
{
	struct TASK *task = task_now();
	unsigned char mode = cmdline[9] - '0';
	if (mode <= 2) {
		task->langmode = mode;
	} else {
		cons_putstr0(cons, "mode number error.\n");
	}
	cons_newline(cons);
	return;
}

void cmd_insmod(struct CONSOLE *cons, int *fat, char *cmdline)
{
	char *filename = cmdline + 7;
	int modsize;
	unsigned char *modbuf;
	Elf32_Ehdr *hdr;
	struct FILEINFO *finfo;
	int i, j;
	int this_module_index, strtab_index, symtab_index, text_index;
	Elf32_Shdr *shdr;
	struct module *this_module;
	char s[256], *strtab;
	Elf32_Sym *symtab;
	const int cs_base = 0x280000;

	/* ファイルを探す */
	finfo = file_search(filename, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);

	if (finfo == 0) {
		cons_putstr0(cons, "no such file: ");
		cons_putstr0(cons, filename);
		return;
	}

	/* ファイルが見つかった場合 */
	modsize = finfo->size;
	modbuf = file_loadfile2(finfo->clustno, &modsize, fat);

	hdr = (Elf32_Ehdr *)modbuf;
	if (!IS_ELF(*hdr)) {
		cons_putstr0(cons, "not a elf binary\n");
		return;
	}

	sprintf(s, "modbuf %p, size %d\n", modbuf, modsize);
	cons_putstr0(cons, s);

	shdr = ELF_GET_SHDR(hdr);
	strtab_index = elf32_find_section(hdr, ".strtab") - shdr;
	strtab = modbuf + shdr[strtab_index].sh_offset;
	symtab_index = elf32_find_section(hdr, ".symtab") - shdr;
	symtab = (Elf32_Sym *)(modbuf + shdr[symtab_index].sh_offset);
	text_index = elf32_find_section(hdr, ".text") - shdr;
	this_module_index = elf32_find_section(hdr, ".this_module") - shdr;
	if (this_module_index == 0) {
		cons_putstr0(cons, "not found .this_module\n");
		return;
	}

	this_module = (struct module *)(modbuf + shdr[this_module_index].sh_offset);

	sprintf(s, "init %p, exit %p, hrb_print %p\n", this_module->init, this_module->exit, &hrb_print);
	cons_putstr0(cons, s);

	// .rel, .rela を探してリロケーション処理
	for (i = 1; i < hdr->e_shnum; i++) {
		Elf32_Shdr *rel_section = ELF_GET_SHDR(hdr) + i;
		char *rel_section_name = ELF_GET_SHSTRTAB(hdr) + rel_section->sh_name;
		if (strncmp(".rel.", rel_section_name, 5) == 0) {
			Elf32_Shdr *target_section = elf32_find_section(hdr, rel_section_name + 4);
			char *target_section_name = ELF_GET_SHSTRTAB(hdr) + target_section->sh_name;
			sprintf(s, "rel %s, tgt %s (off 0x%x)\n", rel_section_name, target_section_name, target_section->sh_offset);
			cons_putstr0(cons, s);

			Elf32_Rel *rel = (Elf32_Rel *)(modbuf + rel_section->sh_offset);
			for (j = 0; j < rel_section->sh_size / sizeof(Elf32_Rel); j++) {
				int sym_index = ELF_R_SYM(rel[j].r_info);
				int r_type = ELF_R_TYPE(rel[j].r_info);
				unsigned int *r_position = (unsigned int *)(modbuf + target_section->sh_offset + rel[j].r_offset);
				unsigned int r_value = 0;
				const char *sym_name = strtab + symtab[sym_index].st_name;

				sprintf(s, "off %08x, inf %08x, sym %d %s\n", rel[j].r_offset, rel[j].r_info, sym_index, strtab + symtab[sym_index].st_name);
				cons_putstr0(cons, s);

				if (symtab[sym_index].st_shndx == 0) {
					// undefined symbol
					struct symbol_table_item *sym = symbol_table;
					while (sym->name != NULL) {
						if (strcmp(sym->name, sym_name) == 0) {
							break;
						}
						sym++;
					}
					if (sym->name == NULL) {
						sprintf(s, "refers to unknown symbol %s\n", sym_name);
						cons_putstr0(cons, s);
						return;
					}
					r_value = sym->address;
				} else {
					r_value = modbuf + shdr[symtab[sym_index].st_shndx].sh_offset + symtab[sym_index].st_value;
				}

				sprintf(s, "pos %08x, val %08x\n", r_position, r_value);
				cons_putstr0(cons, s);

				switch (r_type) {
				case R_386_32: *r_position += r_value; break;
				case R_386_PC32: *r_position += r_value - (unsigned int)r_position; break;
				}
			}
		}
	}

	this_module->init -= 0x280000;
	this_module->exit -= 0x280000;
	//sprintf(s, "mod->init %p, *default_console %p, hrb_print %p\n", this_module->init, *default_console, hrb_print);
	//cons_putstr0(cons, s);

	this_module->init();

	cons_newline(cons);
	return;
}

#define IS_DATA_SEGM(p_flags) (!((p_flags) & PF_X))

static Elf32_Size calc_elf_datasize(Elf32_Ehdr* ehdr)
{
	Elf32_Addr max_vaddr = 0;
	Elf32_Size datasize = 0;
	Elf32_Phdr* phdr = ELF_GET_PHDR(ehdr);
	int i;
	for (i = 0; i < ehdr->e_phnum; i++) {
		if (IS_DATA_SEGM(phdr[i].p_flags) && max_vaddr < phdr[i].p_vaddr) {
			max_vaddr = phdr[i].p_vaddr;
			datasize = max_vaddr + phdr[i].p_memsz;
		}
	}
	return datasize;
}

static void copy_elf_data(void* datasegm, Elf32_Ehdr* ehdr)
{
	Elf32_Phdr* phdr = ELF_GET_PHDR(ehdr);
	int i;
	for (i = 0; i < ehdr->e_phnum; i++) {
		if (IS_DATA_SEGM(phdr[i].p_flags) && phdr[i].p_filesz > 0) {
			memcpy((char*)datasegm + phdr[i].p_vaddr,
				(char*)ehdr + phdr[i].p_offset,
				phdr[i].p_filesz);
		}
	}
}

static Elf32_Size calc_elf_esp(Elf32_Ehdr* ehdr)
{
	Elf32_Shdr* shdr = elf32_find_section(ehdr, ".stack");
	return shdr ? shdr->sh_size : 0;
}

static void app_exec(struct TASK* task,
	void* text, int text_size, void* data, int data_size,
	int eip, int cs, int esp, int ds)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
	int i;
	struct SHEET *sht;

	task->cs_base = (int) text;
	task->ds_base = (int) data;
	set_segmdesc(task->ldt + 0, text_size - 1, (int) text, AR_CODE32_ER + 0x60);
	set_segmdesc(task->ldt + 1, data_size - 1, (int) data, AR_DATA32_RW + 0x60);
	start_app(eip, cs, esp, ds, &(task->tss.esp0));

	for (i = 0; i < MAX_SHEETS; i++) {
		sht = shtctl->sheets0 + i;
		if ((sht->flags & 0x11) == 0x11 && sht->task == task) {
			/* アプリが開きっぱなしにした下じきを発見 */
			sheet_free(sht);	/* 閉じる */
		}
	}
	for (i = 0; i < 8; i++) {	/* クローズしてないファイルをクローズ */
		if (task->fhandle[i].buf != 0) {
			memman_free(memman, (int) task->fhandle[i].buf, task->fhandle[i].size);
			task->fhandle[i].buf = 0;
		}
	}
	timer_cancelall(&task->fifo);
	task->langbyte1 = 0;
}

int cmd_app(struct CONSOLE *cons, int *fat, char *cmdline)
{
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
	struct FILEINFO *finfo;
	char name[13], *p, *q;
	struct TASK *task = task_now();
	int i, namelen, has_dot, segsiz, datsiz, esp, dathrb, appsiz;
	struct SHTCTL *shtctl;
	struct SHEET *sht;

	/* コマンドラインからファイル名を生成 */
	has_dot = 0;
	for (i = 0; i < 12; i++) {
		if (cmdline[i] <= ' ') {
			break;
		} else if (cmdline[i] == '.') {
			has_dot = 1;
		}
		name[i] = cmdline[i];
	}
	name[i] = 0; /* とりあえずファイル名の後ろを0にする */
	namelen = i;

	/* ファイルを探す */
	finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
	if (finfo == 0 && !has_dot && namelen <= 8) {
		/* 見つからなかったので後ろに".HRB"をつけてもう一度探してみる */
		memcpy(name + namelen, ".HRB", 5);
		finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
	}
	if (finfo == 0 && !has_dot && namelen <= 8) {
		memcpy(name + namelen, ".ELF", 5);
		finfo = file_search(name, (struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
	}

	if (finfo != 0) {
		/* ファイルが見つかった場合 */
		appsiz = finfo->size;
		p = file_loadfile2(finfo->clustno, &appsiz, fat);
		if (appsiz >= 36 && strncmp(p + 4, "Hari", 4) == 0 && *p == 0x00) {
			segsiz = *((int *) (p + 0x0000));
			esp    = *((int *) (p + 0x000c));
			datsiz = *((int *) (p + 0x0010));
			dathrb = *((int *) (p + 0x0014));
			q = (char *) memman_alloc_4k(memman, segsiz);
			memcpy(q + esp, p + dathrb, datsiz);
			app_exec(task, p, appsiz, q, segsiz, 0x1b, 0 * 8 + 4, esp, 1 * 8 + 4);
			memman_free_4k(memman, (int) q, segsiz);
		} else if (appsiz >= sizeof(Elf32_Ehdr) && IS_ELF(*(Elf32_Ehdr*)p)) {
			Elf32_Ehdr* ehdr = (Elf32_Ehdr*)p;

			esp = calc_elf_esp(ehdr);
			if (esp == 0) {
				cons_putstr0(cons, "no stack section\n");
				goto elf_fin;
			}
			segsiz = calc_elf_datasize(ehdr);
			q = (char *) memman_alloc_4k(memman, segsiz);
			copy_elf_data(q, ehdr);
			app_exec(task, p, appsiz, q, segsiz, ehdr->e_entry, 0 * 8 + 4, esp, 1 * 8 + 4);
			memman_free_4k(memman, (int) q, segsiz);
elf_fin:;
		} else {
			cons_putstr0(cons, ".hrb file format error.\n");
		}
		memman_free_4k(memman, (int) p, appsiz);
		cons_newline(cons);
		return 1;
	}
	/* ファイルが見つからなかった場合 */
	return 0;
}

static struct MEMMAN* get_elf_memman(struct CONSOLE *cons, struct TASK* task, Elf32_Shdr** p_malloc_sec)
{
	Elf32_Ehdr* ehdr = (Elf32_Ehdr*) task->cs_base;
	if (!IS_ELF(*ehdr)) {
		return 0;
	}
	Elf32_Shdr* malloc_sec = elf32_find_section(ehdr, ".malloc");
	if (p_malloc_sec) {
		*p_malloc_sec = malloc_sec;
	}
	if (!malloc_sec) {
		cons_putstr0(cons, "no malloc section\n");
		return 0;
	}
	return (struct MEMMAN *) (malloc_sec->sh_addr + task->ds_base);
}

int *hrb_api(int edi, int esi, int ebp, int esp, int ebx, int edx, int ecx, int eax)
{
	struct TASK *task = task_now();
	int cs_base = task->cs_base, ds_base = task->ds_base;
	struct CONSOLE *cons = task->cons;
	struct SHTCTL *shtctl = (struct SHTCTL *) *((int *) 0x0fe4);
	struct SHEET *sht;
	struct FIFO32 *sys_fifo = (struct FIFO32 *) *((int *) 0x0fec);
	int *reg = &eax + 1;	/* eaxの次の番地 */
		/* 保存のためのPUSHADを強引に書き換える */
		/* reg[0] : EDI,   reg[1] : ESI,   reg[2] : EBP,   reg[3] : ESP */
		/* reg[4] : EBX,   reg[5] : EDX,   reg[6] : ECX,   reg[7] : EAX */
	int i;
	struct FILEINFO *finfo;
	struct FILEHANDLE *fh;
	struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;

	if (edx == 1) {
		cons_putchar(cons, eax & 0xff, 1);
	} else if (edx == 2) {
		cons_putstr0(cons, (char *) ebx + ds_base);
	} else if (edx == 3) {
		cons_putstr1(cons, (char *) ebx + ds_base, ecx);
	} else if (edx == 4) {
		return &(task->tss.esp0);
	} else if (edx == 5) {
		sht = sheet_alloc(shtctl);
		sht->task = task;
		sht->flags |= 0x10;
		sheet_setbuf(sht, (char *) ebx + ds_base, esi, edi, eax);
		make_window8((char *) ebx + ds_base, esi, edi, (char *) ecx + ds_base, 0);
		sheet_slide(sht, ((shtctl->xsize - esi) / 2) & ~3, (shtctl->ysize - edi) / 2);
		sheet_updown(sht, shtctl->top); /* 今のマウスと同じ高さになるように指定： マウスはこの上になる */
		reg[7] = (int) sht;
	} else if (edx == 6) {
		sht = (struct SHEET *) (ebx & 0xfffffffe);
		putfonts8_asc(sht->buf, sht->bxsize, esi, edi, eax, (char *) ebp + ds_base);
		if ((ebx & 1) == 0) {
			sheet_refresh(sht, esi, edi, esi + ecx * 8, edi + 16);
		}
	} else if (edx == 7) {
		sht = (struct SHEET *) (ebx & 0xfffffffe);
		boxfill8(sht->buf, sht->bxsize, ebp, eax, ecx, esi, edi);
		if ((ebx & 1) == 0) {
			sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
		}
	} else if (edx == 8) {
		Elf32_Ehdr* ehdr = (Elf32_Ehdr*)cs_base;
		if (IS_ELF(*ehdr)) {
			Elf32_Shdr* malloc_sec;
			memman = get_elf_memman(cons, task, &malloc_sec);
			if (!memman) {
				return &(task->tss.esp0);
			}
			memman_init(memman);
			Elf32_Addr dataarea = malloc_sec->sh_addr + 32 * 1024;
			Elf32_Size datasize = calc_elf_datasize(ehdr) - dataarea;
			datasize &= 0xfffffff0;	/* 16バイト単位に */
			memman_free(memman, dataarea, datasize);
		} else {
			memman_init((struct MEMMAN *) (ebx + ds_base));
			ecx &= 0xfffffff0;	/* 16バイト単位に */
			memman_free((struct MEMMAN *) (ebx + ds_base), eax, ecx);
		}
	} else if (edx == 9) {
		ecx = (ecx + 0x0f) & 0xfffffff0; /* 16バイト単位に切り上げ */
		if (IS_ELF(*(Elf32_Ehdr*)cs_base)) {
			memman = get_elf_memman(cons, task, 0);
		} else {
			memman = (struct MEMMAN *) (ebx + ds_base);
		}
		reg[7] = memman_alloc(memman, ecx);
	} else if (edx == 10) {
		ecx = (ecx + 0x0f) & 0xfffffff0; /* 16バイト単位に切り上げ */
		if (IS_ELF(*(Elf32_Ehdr*)cs_base)) {
			memman = get_elf_memman(cons, task, 0);
		} else {
			memman = (struct MEMMAN *) (ebx + ds_base);
		}
		memman_free(memman, eax, ecx);
	} else if (edx == 11) {
		sht = (struct SHEET *) (ebx & 0xfffffffe);
		sht->buf[sht->bxsize * edi + esi] = eax;
		if ((ebx & 1) == 0) {
			sheet_refresh(sht, esi, edi, esi + 1, edi + 1);
		}
	} else if (edx == 12) {
		sht = (struct SHEET *) ebx;
		sheet_refresh(sht, eax, ecx, esi, edi);
	} else if (edx == 13) {
		sht = (struct SHEET *) (ebx & 0xfffffffe);
		hrb_api_linewin(sht, eax, ecx, esi, edi, ebp);
		if ((ebx & 1) == 0) {
			if (eax > esi) {
				i = eax;
				eax = esi;
				esi = i;
			}
			if (ecx > edi) {
				i = ecx;
				ecx = edi;
				edi = i;
			}
			sheet_refresh(sht, eax, ecx, esi + 1, edi + 1);
		}
	} else if (edx == 14) {
		sheet_free((struct SHEET *) ebx);
	} else if (edx == 15) {
		for (;;) {
			io_cli();
			if (fifo32_status(&task->fifo) == 0) {
				if (eax != 0) {
					task_sleep(task);	/* FIFOが空なので寝て待つ */
				} else {
					io_sti();
					reg[7] = -1;
					return 0;
				}
			}
			i = fifo32_get(&task->fifo);
			io_sti();
			if (i <= 1 && cons->sht != 0) { /* カーソル用タイマ */
				/* アプリ実行中はカーソルが出ないので、いつも次は表示用の1を注文しておく */
				timer_init(cons->timer, &task->fifo, 1); /* 次は1を */
				timer_settime(cons->timer, 50);
			}
			if (i == 2) {	/* カーソルON */
				cons->cur_c = COL8_FFFFFF;
			}
			if (i == 3) {	/* カーソルOFF */
				cons->cur_c = -1;
			}
			if (i == 4) {	/* コンソールだけを閉じる */
				timer_cancel(cons->timer);
				io_cli();
				fifo32_put(sys_fifo, cons->sht - shtctl->sheets0 + 2024);	/* 2024〜2279 */
				cons->sht = 0;
				io_sti();
			}
			if (i >= 256) { /* キーボードデータ（タスクA経由）など */
				reg[7] = i - 256;
				return 0;
			}
		}
	} else if (edx == 16) {
		reg[7] = (int) timer_alloc();
		((struct TIMER *) reg[7])->flags2 = 1;	/* 自動キャンセル有効 */
	} else if (edx == 17) {
		timer_init((struct TIMER *) ebx, &task->fifo, eax + 256);
	} else if (edx == 18) {
		timer_settime((struct TIMER *) ebx, eax);
	} else if (edx == 19) {
		timer_free((struct TIMER *) ebx);
	} else if (edx == 20) {
		if (eax == 0) {
			i = io_in8(0x61);
			io_out8(0x61, i & 0x0d);
		} else {
			i = 1193180000 / eax;
			io_out8(0x43, 0xb6);
			io_out8(0x42, i & 0xff);
			io_out8(0x42, i >> 8);
			i = io_in8(0x61);
			io_out8(0x61, (i | 0x03) & 0x0f);
		}
	} else if (edx == 21) {
		for (i = 0; i < 8; i++) {
			if (task->fhandle[i].buf == 0) {
				break;
			}
		}
		fh = &task->fhandle[i];
		reg[7] = 0;
		if (i < 8) {
			finfo = file_search((char *) ebx + ds_base,
					(struct FILEINFO *) (ADR_DISKIMG + 0x002600), 224);
			if (finfo != 0) {
				reg[7] = (int) fh;
				fh->size = finfo->size;
				fh->pos = 0;
				fh->buf = file_loadfile2(finfo->clustno, &fh->size, task->fat);
			}
		}
	} else if (edx == 22) {
		fh = (struct FILEHANDLE *) eax;
		memman_free(memman, (int) fh->buf, fh->size);
		fh->buf = 0;
	} else if (edx == 23) {
		fh = (struct FILEHANDLE *) eax;
		if (ecx == 0) {
			fh->pos = ebx;
		} else if (ecx == 1) {
			fh->pos += ebx;
		} else if (ecx == 2) {
			fh->pos = fh->size + ebx;
		}
		if (fh->pos < 0) {
			fh->pos = 0;
		}
		if (fh->pos > fh->size) {
			fh->pos = fh->size;
		}
	} else if (edx == 24) {
		fh = (struct FILEHANDLE *) eax;
		if (ecx == 0) {
			reg[7] = fh->size;
		} else if (ecx == 1) {
			reg[7] = fh->pos;
		} else if (ecx == 2) {
			reg[7] = fh->pos - fh->size;
		}
	} else if (edx == 25) {
		fh = (struct FILEHANDLE *) eax;
		for (i = 0; i < ecx; i++) {
			if (fh->pos == fh->size) {
				break;
			}
			*((char *) ebx + ds_base + i) = fh->buf[fh->pos];
			fh->pos++;
		}
		reg[7] = i;
	} else if (edx == 26) {
		i = 0;
		for (;;) {
			*((char *) ebx + ds_base + i) =  task->cmdline[i];
			if (task->cmdline[i] == 0) {
				break;
			}
			if (i >= ecx) {
				break;
			}
			i++;
		}
		reg[7] = i;
	} else if (edx == 27) {
		reg[7] = task->langmode;
	}
	return 0;
}

int *inthandler0c(int *esp)
{
	struct TASK *task = task_now();
	struct CONSOLE *cons = task->cons;
	char s[30];
	cons_putstr0(cons, "\nINT 0C :\n Stack Exception.\n");
	sprintf(s, "EIP = %08X\n", esp[11]);
	cons_putstr0(cons, s);
	return &(task->tss.esp0);	/* 異常終了させる */
}

int *inthandler0d(int *esp)
{
	struct TASK *task = task_now();
	struct CONSOLE *cons = task->cons;
	char s[30];
	cons_putstr0(cons, "\nINT 0D :\n General Protected Exception.\n");
	sprintf(s, "EIP = %08X\n", esp[11]);
	cons_putstr0(cons, s);
	return &(task->tss.esp0);	/* 異常終了させる */
}

void hrb_api_linewin(struct SHEET *sht, int x0, int y0, int x1, int y1, int col)
{
	int i, x, y, len, dx, dy;

	dx = x1 - x0;
	dy = y1 - y0;
	x = x0 << 10;
	y = y0 << 10;
	if (dx < 0) {
		dx = - dx;
	}
	if (dy < 0) {
		dy = - dy;
	}
	if (dx >= dy) {
		len = dx + 1;
		if (x0 > x1) {
			dx = -1024;
		} else {
			dx =  1024;
		}
		if (y0 <= y1) {
			dy = ((y1 - y0 + 1) << 10) / len;
		} else {
			dy = ((y1 - y0 - 1) << 10) / len;
		}
	} else {
		len = dy + 1;
		if (y0 > y1) {
			dy = -1024;
		} else {
			dy =  1024;
		}
		if (x0 <= x1) {
			dx = ((x1 - x0 + 1) << 10) / len;
		} else {
			dx = ((x1 - x0 - 1) << 10) / len;
		}
	}

	for (i = 0; i < len; i++) {
		sht->buf[(y >> 10) * sht->bxsize + (x >> 10)] = col;
		x += dx;
		y += dy;
	}

	return;
}
